﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;
using System.Xml.Linq;
using System.Collections.ObjectModel;
using System.Globalization;

[assembly: CLSCompliant(true)]

namespace LotroData
{
    /// <summary>
    /// Defines a LoTRO Character. Provides methods for loading from the data.lotro.com XML stream.
    /// </summary>
    [DebuggerDisplay("Name = {Name}, Level = {Level}, Race = {Race}")]
    public class LotroCharacter
    {
        /// <summary>
        /// Gets or sets the name of the character.
        /// </summary>
        /// <value>A <see cref="System.String"/> value representing the name.</value>
        public string Name { get; protected set; }
        /// <summary>
        /// Gets or sets the world where this character is located.
        /// </summary>
        /// <value>A <see cref="LotroWorld"/> value representing the world.</value>
        public LotroWorld World { get; protected set; }
        /// <summary>
        /// Gets or sets the class type of the character.
        /// </summary>
        /// <value>A <see cref="LotroClass"/> value representing the class.</value>
        public LotroClass Class { get; protected set; }
        /// <summary>
        /// Gets or sets the racial type of the character.
        /// </summary>
        /// <value>A <see cref="LotroRace"/> value representing the race.</value>
        public LotroRace Race { get; protected set; }
        /// <summary>
        /// Gets or sets the level of the character
        /// </summary>
        /// <value>An <c>int</c> value repesenting the level.</value>
        public int Level { get; protected set; }
        /// <summary>
        /// Gets or sets the name of the guild that this character is a part of.
        /// </summary>
        /// <value>A <see cref="System.String"/> value representing the name of the guild.</value>
        public string GuildName { get; protected set; }
        /// <summary>
        /// Gets or sets the guild rank of this character
        /// </summary>
        /// <value>A <see cref="LotroGuildRank"/> value representing the guild rank.</value>
        public LotroGuildRank GuildRank { get; protected set; }
        /// <summary>
        /// Gets or sets the theme of the guild this character is a part of.
        /// </summary>
        /// <value>A <see cref="LotroGuildTheme"/> value represeting the guild theme.</value>
        public LotroGuildTheme GuildTheme { get; protected set; }
        /// <summary>
        /// Gets or sets the PvMP rating points of the character
        /// </summary>
        /// <value>A <c>double</c> value representing the PvMP rating points.</value>
        public double PvmpRatingPoints { get; protected set; }
        /// <summary>
        /// Gets or sets the PvMP glory points that character has accumulated.
        /// </summary>
        /// <value>An <c>int</c> value representing the PvMP glory points.</value>
        public int PvmpGloryPoints { get; protected set; }
        /// <summary>
        /// Gets or sets the PvMP glory rank of the character.
        /// </summary>
        /// <value>An <c>int</c> value representing the PvMP glory rank.</value>
        public int PvmpGloryRank { get; protected set; }
        /// <summary>
        /// Gets or sets a dictionary containing the stats of the character.
        /// </summary>
        /// <value>A generic Dictionary containing the stats.</value>
        public Dictionary<LotroStat, int> Stats { get; private set; }
        /// <summary>
        /// Gets or sets the origin of the character.
        /// </summary>
        /// <value>A <see cref="LotroOrigin"/> value representing the origin.</value>
        public LotroOrigin Origin { get; protected set; }
        /// <summary>
        /// Gets or sets the vocation of the character 
        /// </summary>
        /// <value>A <see cref="LotroVocation"/> value representing the vocation.</value>
        public LotroVocation Vocation { get; protected set; }
        private List<LotroCharacterProfession> _Professions;
        /// <summary>
        /// Gets a readonly collection of the characters professions.
        /// </summary>
        /// <value>A generic ReadOnlyCollection of professions.</value>
        public ReadOnlyCollection<LotroCharacterProfession> Professions { get { return new ReadOnlyCollection<LotroCharacterProfession>(_Professions); } }
        /// <summary>
        /// Gets or sets the equipment that the character has equipped.
        /// </summary>
        /// <value>A generic Dictionary containing the equipment.</value>
        public Dictionary<LotroItemSlot, string[]> Equipment { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="LotroCharacter"/> class.
        /// </summary>
        protected LotroCharacter()
        {
            World = LotroWorld.Unknown;
            Class = LotroClass.Unknown;
            Race = LotroRace.Unknown;
            Level = 0;
            GuildRank = LotroGuildRank.Unknown;

            _Professions = new List<LotroCharacterProfession>(3);
            Equipment = new Dictionary<LotroItemSlot, string[]>();
            Stats = new Dictionary<LotroStat, int>();

            Origin = LotroOrigin.Unknown;
            Vocation = LotroVocation.Unknown;
        }

        /// <summary>
        /// Creates a new <see cref="LotroCharacter"/> from an XML stream.
        /// </summary>
        /// <param name="xmlContainer">A <see cref="XContainer"/> containing the XML stream.</param>
        /// <returns>A new <see cref="LotroCharacter"/>.</returns>
        public static LotroCharacter LoadFromXml(XContainer xmlContainer)
        {
            if (xmlContainer.Element("apiresponse").Element("character") == null) throw new ArgumentException("Invalid XML stream", "xmlContainer");

            var newChar = new LotroCharacter();
            newChar.ParseXml(xmlContainer.Element("apiresponse").Element("character"));
            return newChar;
        }

        /// <summary>
        /// Creates a new <see cref="LotroCharacter"/> from an XML character element.
        /// </summary>
        /// <param name="characterElement">A character <see cref="XElement"/>.</param>
        /// <returns>A new <see cref="LotroCharacter"/>.</returns>
        public static LotroCharacter LoadFromXml(XElement characterElement)
        {
            if (characterElement.Name != "character") throw new ArgumentException("Invalid XML element", "characterElement");

            var newChar = new LotroCharacter();
            newChar.ParseXml(characterElement);
            return newChar;
        }

        /// <summary>
        /// Parses the XML stream
        /// </summary>
        /// <param name="charE">The character <see cref="XElement"/>.</param>
        protected virtual void ParseXml(XElement charE)
        {
            //Character Attributes
            this.Name = charE.Attribute("name").Value;
            this.World = charE.Attribute("world").Value.ParseLotroEnum<LotroWorld>(true);
            this.Race = charE.Attribute("race").Value.ParseLotroEnum<LotroRace>(true);
            this.Class = charE.Attribute("class").Value.ParseLotroEnum<LotroClass>(true);
            this.Level = int.Parse(charE.Attribute("level").Value, CultureInfo.CurrentCulture);

            //Vocation element
            var vocE = charE.Element("vocation");
            //Vocation Attribute
            if (vocE != null)
            {
                this.Vocation = vocE.Attribute("name").Value.ParseLotroEnum<LotroVocation>(true);

                //Professions element
                var profE = vocE.Element("professions");
                //Professions Enum
                var profs = profE.Elements();
                foreach (var prof in profs)
                {
                    this._Professions.Add(new LotroCharacterProfession(
                        prof.Attribute("name").Value.ParseLotroEnum<LotroProfession>(true),
                        int.Parse(prof.Attribute("proficiency").Value, CultureInfo.CurrentCulture),
                        int.Parse(prof.Attribute("mastery").Value, CultureInfo.CurrentCulture)));
                }
            }

            //Guild Element
            var guildE = charE.Element("guild");
            //Guild Attributes
            if (guildE != null)
            {
                this.GuildName = guildE.Attribute("name").Value;
                this.GuildRank = guildE.Attribute("rank").Value.ParseLotroEnum<LotroGuildRank>(true);
                this.GuildTheme = guildE.Attribute("type").Value.ParseLotroEnum<LotroGuildTheme>(true);
            }

            //PVMP Element
            var pvmpE = charE.Element("pvmp");
            if (pvmpE != null)
            {
                //PVMP Attributes
                this.PvmpRatingPoints = double.Parse(pvmpE.Attribute("ratingPoints").Value, CultureInfo.CurrentCulture);
                this.PvmpGloryPoints = int.Parse(pvmpE.Attribute("gloryPoints").Value, CultureInfo.CurrentCulture);
                this.PvmpGloryRank = int.Parse(pvmpE.Attribute("gloryRank").Value, CultureInfo.CurrentCulture);
            }

            //Stats Element
            var statsE = charE.Element("stats");
            if (statsE != null)
            {
                //Stats group
                var stats = statsE.Elements();
                foreach (var stat in stats)
                {
                    this.Stats.Add(
                        stat.Attribute("name").Value.ParseLotroEnum<LotroStat>(true),
                        stat.Attribute("value").Value.ToStatValue());
                }
            }

            //Equipment Element
            var equipE = charE.Element("equipment");
            if (equipE != null)
            {
                //Equipment enumerable
                var items = equipE.Elements();
                foreach (var item in items)
                {
                    this.Equipment.Add(
                        item.Attribute("slot").Value.ParseLotroEnum<LotroItemSlot>(true),
                        new string[]{
                    item.Attribute("name").Value,
                    item.Attribute("lorebookEntry").Value}
                        );
                }
            }
        }
    }
}
